﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.IO;
using System.ComponentModel;

namespace MyServer.Common
{
    /// <summary>
    /// 表示当前数据包列表的状态
    /// </summary>
    public enum PackageCollectionState
    {
        /// <summary>
        /// 表示这个数据包列表刚刚创建尚未初始化
        /// </summary>
        Created,
        /// <summary>
        /// 表示这个数据包列表已初始化但未被操作
        /// </summary>
        Inited,
        /// <summary>
        /// 表示这个数据包列表正准备被发送
        /// </summary>
        ReadyForSend,
        /// <summary>
        /// 表示这个数据包列表正准备被接收
        /// </summary>
        ReadyForRecive,
        /// <summary>
        /// 表示这和数据包列表正准备被操作
        /// </summary>
        ReadyForOperate,
        /// <summary>
        /// 表示这个数据包列表正在被发送
        /// </summary>
        Sending,
        /// <summary>
        /// 表示这个数据包列表正在被接收
        /// </summary>
        Reciving,
        /// <summary>
        /// 表示这个数据包列表正在被操作
        /// </summary>
        Operating,
        /// <summary>
        /// 表示这个数据包列表已操发送完毕
        /// </summary>
        SendCompelete,
        /// <summary>
        /// 表示这个数据包列表已接收完毕
        /// </summary>
        ReciveCompelete,
        /// <summary>
        /// 表示这个数据包列表已操作完毕
        /// </summary>
        OperateCompelete,
        /// <summary>
        /// 表示这个数据包在初始化时出现错误
        /// </summary>
        ErrorInit,
        /// <summary>
        /// 表示这个数据包列表在发送时出现错误
        /// </summary>
        ErrorSend,
        /// <summary>
        /// 表示这个数据包列表在读取时发生错误
        /// </summary>
        ErrorRead,
        /// <summary>
        /// 表示这个数据包列表在操作时发生错误
        /// </summary>
        ErrOperate,
    }

    /// <summary>
    /// 表示数据包类型
    /// </summary>
    public enum PackageCollectionType
    {
        ForSend,
        ForRecive,
        ForOperate
    }

    /// <summary>
    /// 表示一组数据包的集合(支持排序)
    /// </summary>
    public class DataPackageCollection : List<DataPackage>
    {

        #region==========属性==========

        /// <summary>
        /// 获取或设置数据包的名称
        /// </summary>
        [DisplayName("名称")]
        [Description("表示数据包列表的名称")]
        public string PackageName { get; set; }

        /// <summary>
        /// 获取或设置已处理数据包的数量
        /// </summary>
        [ReadOnly(true)]
        [DisplayName("已处理数据报数")]
        [Description("表示数据包列表已被操作的数据包数")]
        public int OperatedPkgCount { get; set; }

        /// <summary>
        /// 获取或设置数据包的处理优先级
        /// </summary>
        [DisplayName("优先级数")]
        [Description("表示数据包列表的优先级数，数字越小优先级越高")]
        public int Precedence { get; set; }

        /// <summary>
        /// 获取当前数据包的剩余优先级量
        /// </summary>
        [DisplayName("剩余优先级数")]
        [Description("表示数据包列表的剩余优先级数，当此值为0时开始操作")]
        public int PrecedenceLeft { get; internal set; }

        /// <summary>
        /// 获取或设置数据包列表包含的信息
        /// </summary>
        [ReadOnly(true)]
        [DisplayName("自定义数据")]
        [Description("表示与数据包关联的对象")]
        [TypeConverter(typeof(ExpandableObjectConverter))]
        public object Tag { get; set; }

        /// <summary>
        /// 获取当前数据包对象是否为接收状态
        /// </summary>
        [DisplayName("是否是接受数据包")]
        [Description("表示当前数据包对象是否为接收状态")]
        public bool IsRecive { get; internal set; }

        private int _Count;
        /// <summary>
        /// 获取当前数据包列表中包含的数据包数量
        /// </summary>
        [DisplayName("数据包数量")]
        [Description("表示当前数据包列表中包含的数据包数量")]
        public new int Count
        {
            get
            {
                if (IsRecive == true)
                    return _Count;
                else
                    return base.Count;
            }
            internal set
            {
                if (IsRecive)
                    _Count = value;
            }
        }

        /// <summary>
        /// 表示当前数据包列表的状态
        /// </summary>
        [DisplayName("状态")]
        [Description("表示当前数据包列表的状态")]
        public PackageCollectionState State { get; internal set; }

        /// <summary>
        /// 表示当前数据包列表的类型
        /// </summary>
        [DisplayName("类型")]
        [Description("表示当前数据包列表的类型")]
        public PackageCollectionType CollectionType { get; private set; }

        /// <summary>
        /// 表示当前数据包最后一次添加数据的时间计数
        /// </summary>
        [DisplayName("最后加入数据时间")]
        [Description("表示当前数据包最后一次加入数据的时间计数")]
        public int LastAddDataTick { get;private set; }

        /// <summary>
        /// 表示当前数据包列表的数据总量（用于单包收发）
        /// </summary>
        [DisplayName("包头数据总量")]
        [Description("表示当前数据包列表的数据总量（用于单包收发）")]
        public int DataCount { get;private set; }

        /// <summary>
        /// 表示当前数据包列表包含的数据数目（用于单包收发）
        /// </summary>
        [DisplayName("包头数据")]
        [Description("表示当前数据包列表包含的数据数目（用于单包收发）")]
        public byte[] Datas { get; private set; }

        #endregion

        #region ========私有变量========

        /// <summary>
        /// 用于线程锁定的对象
        /// </summary>
        private object lockobj = new object();
        private object swaplockobj = new object();

        #endregion

        #region ========公有方法========

        /// <summary>
        /// 对列表中的元素执行快速排序
        /// </summary>
        /// <param name="list">要排序的数据包列表</param>
        /// <param name="start">排序起始项的位置</param>
        /// <param name="end">排序终止项的位置</param>
        public void QuickSort(int start, int end)
        {
            lock (lockobj)
            {
                DataPackageComparer comp = new DataPackageComparer();
                if (end <= start)
                    return;

                int pivotIndex = FindPivot(start, end);
                Swap(pivotIndex, end);

                int k = Partition(start, end, this[end].ID);

                Swap(k, end);

                if (k - start <= 10)
                    Sort(start, k - 1 - start, comp);

                else
                    QuickSort(start, k - 1);

                if (end - k - 1 <= 10)
                    Sort(start, k - 1 - start, comp);
                else
                    QuickSort(k + 1, end);
            }
        }

        /// <summary>
        /// 将集合内指定索引的两个元素执行交换
        /// </summary>
        /// <param name="a">要交换的第一个元素的索引</param>
        /// <param name="b">要交换的第二个元素的索引</param>
        public void Swap(int a, int b)
        {
            lock (swaplockobj)
            {
                DataPackage temp = this[a];
                this[a] = this[b];
                this[b] = temp;
            }
        }

        /// <summary>
        /// 获取数据包列表中数据的总大小
        /// </summary>
        /// <returns></returns>
        public long GetDataSize()
        {
            lock (lockobj)
            {
                long c = 0;
                foreach (DataPackage pkg in this)
                {
                    c += pkg.DataSize;
                }
                return c;
            }
        }

        /// <summary>
        /// 将数据包列表信息封装成字节数组
        /// </summary>
        /// <returns></returns>
        public byte[] ToByte()
        {
            lock (lockobj)
            {
                MemoryStream m = new MemoryStream();
                BinaryWriter bw = new BinaryWriter(m);
                bw.Write(PackageName);
                bw.Write(Precedence);
                bw.Write(Count);
                bw.Write(DataCount);
                if(DataCount!=0)
                {
                    bw.Write(Datas);
                }
                long AddLen = m.Length % 16;
                bw.Write(new byte[AddLen]);
                bw.Flush();
                return m.ToArray();
            }
        }

        /// <summary>
        /// 将字节数组解封为数据包列表
        /// </summary>
        /// <param name="buffer">要解封的字节数组</param>
        public void InitFromByte(byte[] buffer)
        {
            lock (lockobj)
            {
                try
                {
                    MemoryStream m = new MemoryStream(buffer);
                    BinaryReader br = new BinaryReader(m);
                    this.PackageName = br.ReadString();
                    this.Precedence = br.ReadInt32();
                    this._Count = br.ReadInt32(); 
                    this.DataCount = br.ReadInt32();
                    if(this.DataCount!=0)
                    {
                        this.Datas = br.ReadBytes(this.DataCount);
                    }
                    this.Clear();
                    this.State = PackageCollectionState.Inited;
                    this.IsRecive = true;
                    this.LastAddDataTick = Environment.TickCount;
                }
                catch(Exception ex)
                {
#if DEBUG
                    throw;
#else
                this.State = PackageCollectionState.ErrorInit;
#endif
                }
            }
        }

        /// <summary>
        /// 将指定数据包加入到数据包列表中
        /// </summary>
        /// <param name="pkg">要加入的数据包</param>
        public new void Add(DataPackage pkg)
        {
            lock (lockobj)
            {
                if(DataCount>0)
                {
                    throw (new InvalidOperationException("单数据包型列表不允许添加操作！"));
                }
                switch (CollectionType)
                {
                    case PackageCollectionType.ForSend:
                        pkg.PackageName = PackageName;
                        base.Add(pkg);
                        LastAddDataTick = Environment.TickCount;
                        break;
                    case PackageCollectionType.ForRecive:
                        if (pkg.PackageName != PackageName)
                        {
                            throw (new InvalidOperationException("数据包名称与数据包列表名称不匹配！"));
                        }
                        else
                        {
                            base.Add(pkg);
                        }
                        break;
                    case PackageCollectionType.ForOperate:
                        throw (new NotSupportedException("用于操作的数据包只读！"));
                }
            }
        }
        #endregion

        #region ========私有方法========
        private int Partition(int start, int end, long piovtValue)
        {
            start--;
            while (true)
            {

                do { start++; }
                while (this[start].ID < piovtValue);

                if (start > end)
                    break;

                do { end--; }
                while (end > 0 && this[end].ID > piovtValue);

                if (start > end)
                    break;

                Swap(start, end);
            }
            return start;
        }

        private int FindPivot(int start, int end)
        {
            int a = (int)this[start].ID;
            int b = (int)this[end].ID;
            int middle = (start + end) / 2;
            int c = (int)this[middle].ID;

            if ((a - b) * (a - c) < 0)
                return start;
            if ((b - a) * (b - c) < 0)
                return end;

            return middle;
        }
        #endregion

        #region========构造函数========

        /// <summary>
        /// 通过指定的数据包列表名称创建数据包列表
        /// </summary>
        /// <param name="pkgName"></param>
        public DataPackageCollection(string pkgName, PackageCollectionType type)
        {
            lock (lockobj)
            {
                this.PackageName = pkgName;
                this.OperatedPkgCount = 0;
                this.State = PackageCollectionState.Created;
                this.LastAddDataTick = Environment.TickCount;
            }
        }

        /// <summary>
        /// 通过指定的数据包列表名称及数据创建数据包列表
        /// </summary>
        /// <param name="pkgName"></param>
        /// <param name="type"></param>
        /// <param name="Datas"></param>
        public DataPackageCollection(string pkgName, PackageCollectionType type,byte[] Datas)
        {
            lock(lockobj)
            {
                this.PackageName = pkgName;
                this.OperatedPkgCount = 0;
                this.State = PackageCollectionState.Created;
                this.DataCount = Datas.Count();
                this.Datas = Datas;
                this.LastAddDataTick = Environment.TickCount;
            }
        }

        #endregion

    }
}
